/****************************************************************************

    Copyright 2008, 2009  Clark Scheff
    
    This file is part of SchottGunn.

    SchottGunn is free software: you can redistribute it and/or
    modify it under the terms of the GNU General Public License as published
    by the Free Software Foundation version 3 of the License.

    SchottGunn is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with SchottGunn. If not, see http://www.gnu.org/licenses
    
****************************************************************************/

package com.scheffsblend.schottgunn;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import java.util.Random;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.os.Vibrator;
import android.os.PowerManager.WakeLock;

import java.io.IOException;
import java.lang.Math;
import java.util.Vector;

/**
 * @author lithium
 *
 */
public class GraphView extends View implements Runnable
{
	public static final String APP_NAME = "SchotGun";
	public static final int STATE_IDLE = 0;
	public static final int STATE_PUMP1 = 1;
	public static final int STATE_PUMP2 = 2;
	public static final int NUM_SAMPLES = 2;
	
    public static final float PI_OVER_180 = 0.017453293f;
    
    private Bitmap  mBitmap;
    private Bitmap  mGun;
    private Bitmap  mPump;
    private Bitmap	mBackground;
    private Paint   mPaint = new Paint();
    private Canvas  mCanvas = new Canvas();
    private int		mWidth;
    private float   mHeight;
    private boolean mTouched = false;
    private MediaPlayer mMP = new MediaPlayer();
    private SensorManager mSensorManager = null;
    private boolean mLefty = true;
    private Thread mThread = null;
    private boolean mPaused = false;
    private int mMusicPosition = 0;
    Context mContext;
    private WakeLock mWakeLock = null;
    private boolean mKeepScreenOn = true;
    private boolean mLoopStrings = true;
    private int mSenseOffset = 4;
    private int mGunX = 100;
    private int mGunY = 20;
    private int mPumpOffset = 0;
    private SoundPool	mSoundPool;
    private int	mPump1;
    private int mPump2;
    private int mShot;
    private int mState = STATE_IDLE;
	private boolean mFirstRun = true;
	private boolean mVibrate = true;
	private boolean mShowBG = true;
	private boolean mShowInstructions = false;
    private float mGravity[] = new float [] {0.0f, 0.0f};
    private long mLastTime = System.currentTimeMillis();
    private Vibrator mVibrator;
    private Vector<Float> mAccelerations = new Vector<Float>();
  
    public GraphView(Context context, SensorManager sm, boolean first) {
        super(context);
        mContext = context;
        mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
    	mPaint.setFilterBitmap(true);

        final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 
        this.mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "SchotGun");
        mKeepScreenOn = this.getKeepScreenOn();
        mSensorManager = sm;
        mSoundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
        mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
        this.setKeepScreenOn(true);
        mFirstRun = first;
		if(false == mFirstRun) {
			int mask = 0;
			mask = SensorManager.SENSOR_ACCELEROMETER | SensorManager.SENSOR_ORIENTATION;// | SensorManager.SENSOR_ORIENTATION;
			mSensorManager.registerListener(mListener, mask, SensorManager.SENSOR_DELAY_FASTEST);
		}

    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
        mCanvas.setBitmap(mBitmap);
        mCanvas.drawColor(0x000000);
        mWidth = w;
        mHeight = h;
        mGun = BitmapFactory.decodeResource(getResources(), R.drawable.shotgun);
        mPump = BitmapFactory.decodeResource(getResources(), R.drawable.shotgun_pump);
        mBackground = BitmapFactory.decodeResource(getResources(), R.drawable.background);
        initSounds();
        super.onSizeChanged(w, h, oldw, oldh);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        synchronized (this) {
            if (mBitmap != null) {
            	drawScene(canvas);
            }
        }
    }
    
    public void onStop() {
    	// last unregister the sensor listener.
    	mSensorManager.unregisterListener(mListener);

 	   	mSoundPool.release();
    	mPaused = true;
        this.setKeepScreenOn(mKeepScreenOn);
    }
 
    private final SensorListener mListener = new SensorListener() {
        public void onSensorChanged(int sensor, float[] values) {
    		long delta = System.currentTimeMillis() - mLastTime;
        	if(delta >= 200 && sensor == SensorManager.SENSOR_ACCELEROMETER) {
        		values[0] += mGravity[0];
        		values[1] += mGravity[1];
        		if(mLefty)
        			values[0] = -values[0];
        		float xAccel = updateAccelReadings(values[1]);
       			if(delta >= 500 && values[1] <= -mSenseOffset && mState == STATE_IDLE) {
       				mPumpOffset = 70;
       				mSoundPool.play(mPump1, 1, 1, 0, 0, 1);
       				mState = STATE_PUMP1;
       				invalidate();
           			mLastTime = System.currentTimeMillis();
       			}
       			if(delta >= 200 && values[1] >= mSenseOffset && mState == STATE_PUMP1) {
       				mPumpOffset = 0;
       				mSoundPool.play(mPump2, 1, 1, 0, 0, 1);
       				mState = STATE_PUMP2;
       				invalidate();
           			mLastTime = System.currentTimeMillis();
       			}
       			
       			if(delta >= 300 && values[0] >= mSenseOffset && mState == STATE_PUMP2) {
       				Random rand = new Random();
       				float rate = rand.nextFloat() * 0.25f;
       				mSoundPool.play(mShot, 1.5f, 1.5f, 0, 0, 0.85f + rate);
       				if(mVibrate)
       					mVibrator.vibrate(250);
       				mState = STATE_IDLE;
       				invalidate();
           			mLastTime = System.currentTimeMillis();
       			}
        	} else if(sensor == SensorManager.SENSOR_ORIENTATION) {
        		float roll = values[2] * PI_OVER_180;
        		float pitch = values[1] * PI_OVER_180;
        		mGravity[0] = -(float)(SensorManager.GRAVITY_EARTH * Math.sin(roll));
        		mGravity[1] = -(float)(SensorManager.GRAVITY_EARTH * Math.sin(pitch));
        	}
       }
        
        public void onAccuracyChanged(int x, int y) {

        }
    };

    // this method will execute once thread.start is called.  This method
    // will notify the handler that it is time to update the game
    public void run() {
    	while(true) {
   			handler.sendEmptyMessage(0);
   			try {
				// allow the thread to sleep a bit and allow other threads to run
   				// 17 milliseconds will allow for a frame rate of about 60 FPS.
   				Thread.sleep(20);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    }
    
    private Handler handler = new Handler() {
    	@Override
    	public void handleMessage(Message msg) {
    	
       		invalidate();
    	}
    };
    
    @Override
	public boolean onTouchEvent(MotionEvent event) {
    	int action = event.getAction();
    	if(action == MotionEvent.ACTION_DOWN) {
    		if(mFirstRun || mShowInstructions) {
    			mFirstRun = false;
    			mShowInstructions = false;
    			int mask = 0;
    			mask = SensorManager.SENSOR_ACCELEROMETER | SensorManager.SENSOR_ORIENTATION;// | SensorManager.SENSOR_ORIENTATION;
    			mSensorManager.registerListener(mListener, mask, SensorManager.SENSOR_DELAY_GAME);
    			invalidate();
    		} else if(mState == STATE_PUMP2) {
    			Random rand = new Random();
    			float rate = rand.nextFloat() * 0.25f;
    			mSoundPool.play(mShot, 1, 1, 0, 0, 0.85f + rate);
   				if(mVibrate)
   					mVibrator.vibrate(250);
    			mState = STATE_IDLE;
    			invalidate();
    		}
    	}
   	
    	return true;
    }

    private void drawScene(Canvas canvas) {
    	if(mFirstRun || mShowInstructions) {
    		Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.howto);
    		canvas.drawBitmap(bmp, 0, 0, mPaint);
    	} else {
    		if(mShowBG)
    			canvas.drawBitmap(mBackground, 0, 0, mPaint);
    		Matrix m = new Matrix();
    		if(mLefty)
    			m.preScale(-1, 1, mGun.getWidth()/2, mGun.getHeight()/2);
    		m.postTranslate(mGunX, mGunY);
    		canvas.drawBitmap(mGun, m, mPaint);
//    		canvas.drawBitmap(mGun, mGunX, mGunY, mPaint);
    		m.reset();
    		if(mLefty) {
    			m.preScale(1, -1, mPump.getWidth()/2, mPump.getHeight()/2);
    			m.postTranslate(mGunX+15, mGunY+40+mPumpOffset);
    		} else
    			m.postTranslate(mGunX+68, mGunY+40+mPumpOffset);
    		canvas.drawBitmap(mPump, m, mPaint);
//    		canvas.drawBitmap(mPump, mGunX + 68, mGunY + 40 +mPumpOffset, mPaint);
    		Bitmap bitmap;
    		if(mState == STATE_PUMP2)
    			bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.shell_loaded);
    		else if (mState == STATE_PUMP1)
    			bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.shell_cocked);
    		else
    			bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.shell_empty);
    		m.reset();
    		if(mLefty) {
    			m.postTranslate(mWidth-bitmap.getWidth()-2, 172);
    		} else
    			m.postTranslate(2, 172);
    		canvas.drawBitmap(bitmap, m, mPaint);
    	}
    }
   
   public void setLefty(boolean lefty) {
	   mLefty = lefty;
	   invalidate();
   }
   
   public boolean getLefty() {
	   return mLefty;
   }
   
   public void setLoopStrings(boolean loop) {
	   mLoopStrings = loop;
   }
   
   public boolean getLoopStrings() {
	   return mLoopStrings;
   }
   
   public void setSenseOffset(int offset) {
	   if(offset < 2)
		   offset = 2;
	   else if(offset > 12)
		   offset = 12;
	   mSenseOffset = offset;
   }
   
   public int getSenseOffset() {
	   return mSenseOffset;
   }
   
   public void pause() {
	   mPaused = true;
       this.setKeepScreenOn(mKeepScreenOn);
	   mSensorManager.unregisterListener(mListener);
   }
   
   public void resume() {
	   mPaused = false;
	   this.setKeepScreenOn(true);
	   mSensorManager.registerListener(mListener, SensorManager.SENSOR_ACCELEROMETER | SensorManager.SENSOR_ORIENTATION, 
				   							SensorManager.SENSOR_DELAY_GAME);
   }

   private void initSounds() {
		mPump1 = mSoundPool.load(this.getContext(), R.raw.pump1, 0);
		mPump2 = mSoundPool.load(this.getContext(), R.raw.pump2, 0);
		mShot = mSoundPool.load(this.getContext(), R.raw.shot, 0);
	}
	
   public void setFirstRun(boolean first) {
	   mFirstRun = first;
	   if(mFirstRun) {
		   mSensorManager.unregisterListener(mListener);
		   mPumpOffset = 0;
		   invalidate();
	   }
   }
   
   public boolean getFirstRun() {
	   return mFirstRun;
   }
   
   public void showInstructions() {
	   mSensorManager.unregisterListener(mListener);
	   mPumpOffset = 0;
	   mShowInstructions = true;
	   invalidate();
   }
   
   public boolean getVibrate() {
	   return mVibrate;
   }
   
   public void setVibrate(boolean vibrate) {
	   mVibrate = vibrate;
   }

   public boolean getShowBG() {
	   return mShowBG;
   }
   
   public void setShowBG(boolean bg) {
	   mShowBG = bg;
	   invalidate();
   }

   private float updateAccelReadings(float accel) {
   	if(mAccelerations.size() == NUM_SAMPLES)
   		mAccelerations.removeElementAt(0);
   	
   	mAccelerations.add(mAccelerations.size(), accel);
   	
   	if(mAccelerations.size() < NUM_SAMPLES)
   		return 0;
   	
   	float avg = 0.0f;
	for(int i = 0; i < mAccelerations.size(); i++) {
   		avg = avg + mAccelerations.get(i);
   		
   	}
	avg = avg / 3.0f;
   	
   	return avg;
   }
   
}

